
package com.ccloud.common.im.server;

import com.alibaba.fastjson.JSON;
import com.ccloud.common.im.constant.CIMConstant;
import com.ccloud.common.im.handler.CIMRequestHandler;
import com.ccloud.common.im.handler.CommonChannelEventHandler;
import com.ccloud.common.im.handler.HeartbeatHandler;
import com.ccloud.common.im.model.CIMMsg;
import com.ccloud.common.im.model.CIMSession;
import com.ccloud.common.im.model.HeartbeatRequest;
import com.ccloud.common.im.model.HeartbeatResponse;
import com.ccloud.common.im.util.MessageUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


/**
 * @author chentl
 * @version V1.0
 * @Project IM-SDK
 * @Title CIMNioSocketServer.java
 * @Description 自定义IM服务器配置类
 * @Package com.whdsj.sdk.im.server.handler
 * @date 2020/9/6 4:51 下午
 */
@Slf4j
public class CIMNioSocketServer {

    private static final Logger LOGGER = LoggerFactory.getLogger(CIMNioSocketServer.class);

    /**
     * 读空闲时间(秒)
     */
    public static final int READ_IDLE_TIME = 30;

    /**
     * 写接空闲时间(秒)
     */
    public static final int WRITE_IDLE_TIME = 30;

    //内置特殊处理handler
    private final HashMap<String, CIMRequestHandler> innerHandlerMap = new HashMap<>();


    /**
     * @description 线程安全的所有频道组 @author chentl @date 2020/9/7 3:42 下午
     **/
    private final ConcurrentHashMap<String, Channel> channelGroup = new ConcurrentHashMap<>();

    private EventLoopGroup webBossGroup;
    private EventLoopGroup webWorkerGroup;

    private final String webHost;
    private final Integer webPort;

    private final CIMRequestHandler outerRequestHandler;

    private final ChannelHandler defaultChannelEventHandler;

    /**
     * @return
     * @author chentl
     * @description 绑定构造
     * @date 2020/9/6 2:59 下午
     * @params [builder]
     */
    public CIMNioSocketServer(Builder builder) {
        this.webPort = builder.webPort;
        this.webHost = builder.webHost;
        this.outerRequestHandler = builder.outerRequestHandler;
        this.defaultChannelEventHandler = new CommonChannelEventHandler(outerRequestHandler,innerHandlerMap,channelGroup);

    }


    /**
     * @author chentl
     * @description 绑定服务构造实现类
     * @date 2020/9/6 2:58 下午
     * @params
     * @return
     */
    public static class Builder {

        private String webHost;
        private Integer webPort;
        private CIMRequestHandler outerRequestHandler;

        public Builder wsConfig(String host, Integer port) {
            this.webHost = host;
            this.webPort = port;
            return this;
        }

        /**
         * @return com.whdsj.sdk.im.server.handler.CIMNioSocketServer.Builder
         * @author chentl
         * @description 设置应用层的消息处理handler
         * @date 2020/9/6 2:59 下午
         * @params [outerRequestHandler]
         */
        public Builder setOuterRequestHandler(CIMRequestHandler outerRequestHandler) {
            this.outerRequestHandler = outerRequestHandler;
            return this;
        }

        /**
         * @return com.whdsj.sdk.im.server.handler.CIMNioSocketServer
         * @author chentl
         * @description 绑定函数
         * @date 2020/9/6 3:00 下午
         * @params []
         */
        public CIMNioSocketServer build() {
            return new CIMNioSocketServer(this);
        }

    }

    /**
     * @return void
     * @author chentl
     * @description Netty服务绑定WEB端口
     * @date 2020/9/6 3:00 下午
     * @params []
     */
    public void bind() {
        innerHandlerMap.put(CIMConstant.CLIENT_HEARTBEAT, new HeartbeatHandler());

        if (webPort != null) {
            bindWebPort();
        }
    }

    /**
     * @return void
     * @author chentl
     * @description 绑定web端口
     * @date 2020/9/6 3:04 下午
     * @params []
     */
    private void bindWebPort() {
        webBossGroup = new NioEventLoopGroup();
        webWorkerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = createServerBootstrap(webBossGroup, webWorkerGroup);
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

            @Override
            public void initChannel(SocketChannel ch) {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new HttpServerCodec());
                pipeline.addLast(new ChunkedWriteHandler());
                pipeline.addLast(new HttpObjectAggregator(65536));
                pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                pipeline.addLast(new IdleStateHandler(READ_IDLE_TIME, WRITE_IDLE_TIME, 0, TimeUnit.SECONDS));
                pipeline.addLast(new LoggingHandler(LogLevel.DEBUG));
                pipeline.addLast(defaultChannelEventHandler);
            }

        });

        ChannelFuture channelFuture = bootstrap.bind(webHost, webPort).syncUninterruptibly();
        channelFuture.channel().newSucceededFuture().addListener(future -> {
            String logBanner = "\n\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "*                   Websocket Server started on host {} port {}.                     \n" +
                    "*                                                                                   *\n" +
                    "*                                                                                   *\n" +
                    "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n";
            LOGGER.info(logBanner, webHost, webPort);
        });
        channelFuture.channel().closeFuture().addListener(future -> this.destroy(webBossGroup, webWorkerGroup));
    }

    /**
     * @return io.netty.bootstrap.ServerBootstrap
     * @author chentl
     * @description 初始化服务参数
     * @date 2020/9/6 3:01 下午
     * @params [bossGroup, workerGroup]
     */
    private ServerBootstrap createServerBootstrap(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.option(ChannelOption.SO_BACKLOG, CIMConstant.SO_BACKLOG)
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        return bootstrap;
    }


    /**
     * @return void
     * @author chentl
     * @description 销毁主从线程接收对象, 释放资源
     * @date 2020/9/6 3:03 下午
     * @params [bossGroup, workerGroup]
     */
    public void destroy(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
        if (bossGroup != null && !bossGroup.isShuttingDown() && !bossGroup.isShutdown()) {
            try {
                bossGroup.shutdownGracefully();
            } catch (Exception ignore) {
            }
        }

        if (workerGroup != null && !workerGroup.isShuttingDown() && !workerGroup.isShutdown()) {
            try {
                workerGroup.shutdownGracefully();
            } catch (Exception ignore) {
            }
        }
    }

    /**
     * 关闭长连接服务
     */
    public void destroy() {
        this.destroy(webBossGroup, webWorkerGroup);
    }

    /**
     * @return io.netty.channel.Channel
     * @author chentl
     * @description 获取当前Channel
     * @date 2020/9/6 3:49 下午
     * @params [id]
     */
    public Channel getManagedSession(String id) {
        if (id == null) {
            return null;
        }
        return channelGroup.get(id);
    }




}
